// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/policy/dlp/dlp_confidential_contents.h"

#include <string>

#include "base/test/test_mock_time_task_runner.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace policy {

constexpr DlpRulesManager::Restriction kRestriction =
    DlpRulesManager::Restriction::kPrinting;

class DlpConfidentialContentsTest : public testing::Test {
 public:
  const std::u16string title1 = u"Example1";
  const std::u16string title2 = u"Example2";
  const std::u16string title3 = u"Example3";
  const GURL url1 = GURL("https://example1.com");
  const GURL url2 = GURL("https://example2.com");
  const GURL url3 = GURL("https://example3.com");

  DlpConfidentialContentsTest()
      : profile_(std::make_unique<TestingProfile>()) {}
  DlpConfidentialContentsTest(const DlpConfidentialContentsTest&) = delete;
  DlpConfidentialContentsTest& operator=(const DlpConfidentialContentsTest&) =
      delete;
  ~DlpConfidentialContentsTest() override = default;

  std::unique_ptr<content::WebContents> CreateWebContents(
      const std::u16string& title,
      const GURL& url) {
    std::unique_ptr<content::WebContents> web_contents =
        content::WebContentsTester::CreateTestWebContents(profile_.get(),
                                                          nullptr);
    content::WebContentsTester* tester =
        content::WebContentsTester::For(web_contents.get());
    tester->SetTitle(title);
    tester->SetLastCommittedURL(url);
    return web_contents;
  }

  DlpConfidentialContent CreateConfidentialContent(const std::u16string& title,
                                                   const GURL& url) {
    return DlpConfidentialContent(CreateWebContents(title, url).get());
  }

  // Helper to check whether |contents| has an element corresponding to
  // |web_contents|.
  bool Contains(const DlpConfidentialContents& contents,
                content::WebContents* web_contents) {
    auto iter = std::find_if(contents.GetContents().begin(),
                             contents.GetContents().end(),
                             [&](const DlpConfidentialContent& content) {
                               return content.url.EqualsIgnoringRef(
                                   web_contents->GetLastCommittedURL());
                             });
    return iter != contents.GetContents().end();
  }

 private:
  content::BrowserTaskEnvironment task_environment_;
  content::RenderViewHostTestEnabler render_view_host_test_enabler_;
  const std::unique_ptr<TestingProfile> profile_;
};

TEST_F(DlpConfidentialContentsTest, EmptyContents) {
  DlpConfidentialContents contents;
  EXPECT_TRUE(contents.IsEmpty());
}

TEST_F(DlpConfidentialContentsTest, DuplicateConfidentialDataAdded) {
  DlpConfidentialContents contents;
  auto web_contents = CreateWebContents(title1, url1);
  contents.Add(web_contents.get());
  contents.Add(web_contents.get());
  EXPECT_EQ(contents.GetContents().size(), 1u);
  EXPECT_TRUE(Contains(contents, web_contents.get()));
}

TEST_F(DlpConfidentialContentsTest, ClearAndAdd) {
  DlpConfidentialContents contents;

  auto web_contents1 = CreateWebContents(title1, url1);
  auto web_contents2 = CreateWebContents(title2, url2);
  auto web_contents3 = CreateWebContents(title3, url3);

  contents.Add(web_contents1.get());
  contents.Add(web_contents2.get());
  EXPECT_EQ(contents.GetContents().size(), 2u);

  contents.ClearAndAdd(CreateWebContents(title3, url3).get());
  EXPECT_EQ(contents.GetContents().size(), 1u);
  EXPECT_FALSE(Contains(contents, web_contents1.get()));
  EXPECT_FALSE(Contains(contents, web_contents2.get()));
  EXPECT_TRUE(Contains(contents, web_contents3.get()));
}

TEST_F(DlpConfidentialContentsTest, UnionShouldAddUniqueItems) {
  DlpConfidentialContents contents1;
  DlpConfidentialContents contents2;

  auto web_contents1 = CreateWebContents(title1, url1);
  auto web_contents2 = CreateWebContents(title2, url2);
  auto web_contents3 = CreateWebContents(title3, url3);

  contents1.Add(web_contents1.get());
  contents1.Add(web_contents2.get());

  contents2.Add(web_contents1.get());
  contents2.Add(web_contents3.get());

  EXPECT_EQ(contents1.GetContents().size(), 2u);
  EXPECT_EQ(contents2.GetContents().size(), 2u);
  EXPECT_FALSE(Contains(contents1, web_contents3.get()));

  contents1.UnionWith(contents2);

  EXPECT_EQ(contents1.GetContents().size(), 3u);
  EXPECT_EQ(contents2.GetContents().size(), 2u);
  EXPECT_TRUE(Contains(contents1, web_contents3.get()));
}

TEST_F(DlpConfidentialContentsTest, CacheEvictsAfterTimeout) {
  scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
      base::MakeRefCounted<base::TestMockTimeTaskRunner>();
  DlpConfidentialContentsCache cache;
  cache.SetTaskRunnerForTesting(task_runner);

  DlpConfidentialContent content = CreateConfidentialContent(title1, url1);

  cache.Cache(content, kRestriction);
  EXPECT_TRUE(cache.Contains(content, kRestriction));
  task_runner->FastForwardBy(DlpConfidentialContentsCache::GetCacheTimeout());
  EXPECT_FALSE(cache.Contains(content, kRestriction));
}

TEST_F(DlpConfidentialContentsTest, CacheEvictsWhenFull) {
  DlpConfidentialContentsCache cache;
  cache.SetCacheSizeLimitForTesting(1);

  DlpConfidentialContent content1 = CreateConfidentialContent(title1, url1);
  DlpConfidentialContent content2 = CreateConfidentialContent(title2, url2);

  cache.Cache(content1, kRestriction);
  EXPECT_TRUE(cache.Contains(content1, kRestriction));
  cache.Cache(content2, kRestriction);
  EXPECT_FALSE(cache.Contains(content1, kRestriction));
  EXPECT_TRUE(cache.Contains(content2, kRestriction));
}

TEST_F(DlpConfidentialContentsTest, CacheRemovesDuplicates) {
  DlpConfidentialContentsCache cache;

  DlpConfidentialContent content = CreateConfidentialContent(title1, url1);

  cache.Cache(content, kRestriction);
  cache.Cache(content, kRestriction);
  EXPECT_EQ(cache.GetSizeForTesting(), 1u);
}

}  // namespace policy
